/*
 * @(#)XfuzzyDataSet.java        1.0 2009/03/25
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfds;

import java.util.Vector;
import xfuzzy.lang.Universe;

/**
 * Clase que describe un conjunto de datos
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdsDataSet  {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Instancias del conjunto de datos (atributos de entrada)
	 */
	public double[][] input;
	
	/**
	 * Instancias del conjunto de datos (atributos de salida)
	 */
	public double[][] output;
	
	/**
	 * Rango de los atributos de salida
	 */
	public double[] range;
	
	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Nombre del conjunto de datos
	 */
	private String relation;
	
	/**
	 * Lista de atributos
	 */
	private XfdsAttribute[] attr;
			
	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public XfdsDataSet(String relation) {
		this.relation = relation;
		this.attr = new XfdsAttribute[0];
	}
	
	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Obtiene el nombre del conjunto de datos
	 */
	public String getName() {
		return this.relation;
	}
	
	/**
	 * Asigna la lista de atributos
	 * @param na
	 */
	public void setAttributes(XfdsAttribute[] na) {
		this.attr = na;
	}
	
	/**
	 * A�ade un atributo a la lista
	 * @param na
	 */
	public void addAttribute(XfdsAttribute na) {
		na.setInput(false);
		if(attr.length >0) attr[attr.length-1].setInput(true);
		XfdsAttribute[] aux = new XfdsAttribute[attr.length+1];
		System.arraycopy(attr, 0, aux, 0, attr.length);
		aux[attr.length] = na;
		attr = aux;
	}

	/**
	 * Obtiene la lista de atributos del conjunto de datos
	 */
	public XfdsAttribute[] getAttributes() {
		return this.attr;
	}
	
	/**
	 * Obtiene la lista de atributos de entrada del conjunto de datos
	 */
	public XfdsAttribute[] getInputAttributes() {
		int count = 0;
		for(int i=0; i<attr.length; i++) if(attr[i].isInput()) count++;
		XfdsAttribute[] list = new XfdsAttribute[count];
		for(int i=0,j=0; i<attr.length; i++) if(attr[i].isInput()) {
			list[j] = attr[i];
			j++;
		}

		return list;
	}
	
	/**
	 * Obtiene la lista de atributos de salida del conjunto de datos
	 */
	public XfdsAttribute[] getOutputAttributes() {
		int count = 0;
		for(int i=0; i<attr.length; i++) if(!attr[i].isInput()) count++;
		XfdsAttribute[] list = new XfdsAttribute[count];
		for(int i=0,j=0; i<attr.length; i++) if(!attr[i].isInput()) {
			list[j] = attr[i];
			j++;
		}

		return list;
	}
	
	/**
	 * A�ade una instancia al conjunto de datos
	 * @param instance
	 */
	public void addInstance(Vector<String> instance) {	
		int numInputs = 0, numOutputs = 0;
		if(input == null) { // Primera instancia del conjunto
			for(int i=0; i<attr.length; i++) {
				if(attr[i].isInput()) numInputs++;
				else numOutputs++;
			}
			this.input = new double[1][];
			this.output = new double[1][];
			
			this.range = new double[numOutputs];
			for(int i=0, o=0; i<attr.length; i++) {
				if(!attr[i].isInput()) { this.range[o] = attr[i].getRange(); o++; }
			}
			
		} else {
			numInputs = input[0].length;
			numOutputs = output[0].length;
			
			double[][] iaux = new double[input.length+1][];
			System.arraycopy(input, 0, iaux, 0, input.length);
			this.input = iaux; 
			
			double[][] oaux = new double[output.length+1][];
			System.arraycopy(output, 0, oaux, 0, output.length);
			this.output = oaux; 
		}
		
		double[] ival = new double[numInputs];
		double[] oval = new double[numOutputs];
		
		int size = instance.size();
		for(int i=0,j=0,o=0; i<size; i++) {
			double val = attr[i].getValue(instance.elementAt(i));
			if(attr[i].isInput()) { ival[j] = val; j++; }
			else { oval[o] = val; o++; }
		}
		
		this.input[input.length-1] = ival;
		this.output[output.length-1] = oval;
	}
	
	/**
	 * Indica cuales de los atributos son entradas  
	 * @param inputs
	 */
	public void setInputs(Vector<String> inputs) {
		int size = inputs.size();
		for(int i=0;i<size; i++) {
			String attrname = inputs.elementAt(i);
			XfdsAttribute iattr = getAttribute(attrname);
			if(iattr != null) iattr.setInput(true);
		}
	}
	
	/**
	 * Indica cuales de los atributos son salidas
	 * @param outputs
	 */
	public void setOutputs(Vector<String> outputs) {
		int size = outputs.size();
		for(int i=0;i<size; i++) {
			String attrname = outputs.elementAt(i);
			XfdsAttribute oattr = getAttribute(attrname);
			if(oattr != null) oattr.setInput(false);
		}		
	}
	
	/**
	 * Obtiene un objeto con una �nica instancia
	 */
	public XfdsDataSet getSingle(int p) {
		XfdsDataSet single = new XfdsDataSet(this.relation);
		single.setAttributes(this.attr);
		single.input = new double[1][]; single.input[0] = this.input[p];
		single.output = new double[1][]; single.output[0] = this.output[p];
		single.range = this.range;
		return single;
	}

	/**
	 * Obtiene el n�mero de atributos de entrada del conjunto de datos
	 * @return
	 */
	public int getNumberOfInputs() {
		int count = 0;
		for(int i=0; i<attr.length; i++) if(attr[i].isInput()) count++;
		return count;
	}
	
	/**
	 * Obtiene el n�mero de atributos de salida del conjunto de datos
	 * @return
	 */
	public int getNumberOfOutputs() {
		int count = 0;
		for(int i=0; i<attr.length; i++) if(!attr[i].isInput()) count++;
		return count;		
	}
	
	/**
	 * Calcula el universo de discurso de la variable i-esima
	 */
	public Universe getUniverse(int var, boolean inputvar) {
		for(int i=0,j=0; i<attr.length; i++) {
			if(attr[i].isInput() == inputvar) {
				if(j==var) return attr[i].getUniverse();
				else j++;
			}
		}
		return null;
	}

	/**
	 * Estudia si se trata de un  conjunto de datos de clasificaci�n
	 * @return
	 */
	public boolean isAClassification() {
		for(int i=0; i<attr.length; i++) {
			if(!attr[i].isInput() && attr[i].getType() instanceof XfdsEnumDataType ) return true;
		}
		return false;
	}

	/**
	 * Estudia si se trata de un conjunto de datos de regresi�n
	 * @return
	 */
	public boolean isARegression() {
		for(int i=0; i<attr.length; i++) {
			if(!attr[i].isInput() && !(attr[i].getType() instanceof XfdsEnumDataType) ) return true;
		}
		return false;
	}

	/**
	 * Asigna los rangos a aquellos atributos que no los tienen preasignados
	 */
	public void setRanges() {
		for(int a=0,i=0,o=0; a<attr.length; a++) {
			if(attr[a].isInput()) {
				if(!attr[a].getType().isRanged()) {
					double min = input[0][i];
					double max = input[0][i];
					for(int k=1; k<input.length; k++) {
						if(input[k][i] < min) min = input[k][i];
						if(input[k][i] > max) max = input[k][i];
					}
					attr[a].getType().setRange(min,max);
				}
				i++; 
			}
			else { 
				if(!attr[a].getType().isRanged()) {
					double min = output[0][o];
					double max = output[0][o];
					for(int k=1; k<output.length; k++) {
						if(output[k][o] < min) min = output[k][o];
						if(output[k][o] > max) max = output[k][o];
					}
					attr[a].getType().setRange(min,max);
				}
				o++; 
			}
		}
	}
	
	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Busca un atributo a partir de su nombre
	 */
	private XfdsAttribute getAttribute(String attname) {
		for(int i=0; i<attr.length; i++) {
			if(attr[i].getName().equals(attname)) return attr[i];
		}
		return null;
	}

}
